package ace.cmp.spring.data.jdbc.querydsl.core;

import static org.springframework.data.repository.core.support.RepositoryFragment.implemented;

import ace.cmp.spring.data.jdbc.querydsl.core.repository.QuerydslJdbcBaseFragment;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.QuerydslJdbcBaseFragmentImpl;
import com.infobip.spring.data.common.QuerydslExpressionFactory;
import com.infobip.spring.data.jdbc.QuerydslJdbcPredicateExecutor;
import com.infobip.spring.data.jdbc.QuerydslJdbcRepositoryFactory;
import com.querydsl.sql.SQLQueryFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;

/**
 * @author caspar
 * @date 2023/4/3 19:42
 */
public class QuerydslJdbcRepositoryExtendFactory extends QuerydslJdbcRepositoryFactory {
  private final Class<?> REPOSITORY_TARGET_TYPE = QuerydslJdbcBaseFragment.class;
  private final QuerydslExpressionFactory querydslExpressionFactory =
      new QuerydslExpressionFactory(REPOSITORY_TARGET_TYPE);
  private final SQLQueryFactory sqlQueryFactory;
  private final RelationalMappingContext context;
  private final QuerydslJdbcRepositoryExtendFactoryInjectHelper
      querydslJdbcRepositoryExtendFactoryInjectHelper;

  public QuerydslJdbcRepositoryExtendFactory(
      DataAccessStrategy dataAccessStrategy,
      RelationalMappingContext context,
      JdbcConverter converter,
      Dialect dialect,
      ApplicationEventPublisher publisher,
      NamedParameterJdbcOperations operations,
      SQLQueryFactory sqlQueryFactory,
      QuerydslJdbcRepositoryExtendFactoryInjectHelper
          querydslJdbcRepositoryExtendFactoryInjectHelper) {
    super(dataAccessStrategy, context, converter, dialect, publisher, operations, sqlQueryFactory);
    this.sqlQueryFactory = sqlQueryFactory;
    this.context = context;
    this.querydslJdbcRepositoryExtendFactoryInjectHelper =
        querydslJdbcRepositoryExtendFactoryInjectHelper;
  }

  @Override
  protected RepositoryComposition.RepositoryFragments getRepositoryFragments(
      RepositoryMetadata metadata) {

    RepositoryComposition.RepositoryFragments fragments = super.getRepositoryFragments(metadata);

    Class repositoryInterface = metadata.getRepositoryInterface();

    if (!REPOSITORY_TARGET_TYPE.isAssignableFrom(repositoryInterface)) {
      return fragments;
    }

    QuerydslJdbcPredicateExecutor querydslJdbcPredicateExecutor =
        fragments
            .get()
            .filter(
                p ->
                    QuerydslJdbcPredicateExecutor.class.isAssignableFrom(
                        p.getImplementation().get().getClass()))
            .findFirst()
            .map(p -> (QuerydslJdbcPredicateExecutor) p.getImplementation().get())
            .orElseThrow();

    QuerydslJdbcBaseFragment querydslJdbcBaseFragment =
        this.createQuerydslJdbcBaseFragment(
            metadata.getDomainType(),
            metadata.getIdType(),
            sqlQueryFactory,
            querydslJdbcPredicateExecutor);

    return fragments.append(implemented(querydslJdbcBaseFragment));
  }

  private QuerydslJdbcBaseFragment createQuerydslJdbcBaseFragment(
      Class entityType,
      Class idType,
      SQLQueryFactory sqlQueryFactory,
      QuerydslJdbcPredicateExecutor predicateExecutor) {
    return new QuerydslJdbcBaseFragmentImpl(
        entityType,
        idType,
        sqlQueryFactory,
        predicateExecutor,
        querydslJdbcRepositoryExtendFactoryInjectHelper);
  }
}
